/*
 * Copyright (C) 2012  Romain DUBOIS
 * 
 * This file is part of NeMoS - Network and Modular Software
 * 
 * NeMoS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * NeMoS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with NeMoS.  If not, see <http://www.gnu.org/licenses/>.
 */
package nemos.sample.greetings.console;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;

import nemos.resources.IResourcesManager;
import nemos.util.i18n.ITranslator;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.service.command.CommandProcessor;
import org.apache.felix.service.command.CommandSession;
import org.apache.felix.service.command.Descriptor;

/**
 * SampleCommand : The sample commands class.
 * 
 * <p>
 * This class exposes two Gogo commands for the greetings sample:
 * <ul>
 * <li>locale change</li>
 * <li>greetings</li>
 * </ul>
 * 
 * @author Romain DUBOIS
 */
@Service(SampleCommand.class)
@Properties({ @Property(name = CommandProcessor.COMMAND_FUNCTION, value = { "greet", "locale" }),
        @Property(name = CommandProcessor.COMMAND_SCOPE, value = "greet") })
@Component
public class SampleCommand {

    /** The greetings message key */
    private static final String GREET_TEXT = "greet.text"; //$NON-NLS-1$

    /** The current locale message key */
    private static final String LOCALE_CURRENT = "locale.current"; //$NON-NLS-1$
    /** The locale change message key */
    private static final String LOCALE_CHANGED = "locale.changed"; //$NON-NLS-1$
    /** The locale error message key */
    private static final String LOCALE_ERROR = "locale.error"; //$NON-NLS-1$

    /** The translator */
    @Reference(name = "translator")
    private ITranslator mTranslator;
    /** The resources manager */
    @Reference(name = "resources")
    private IResourcesManager mResourcesManager;

    /**
     * Constructor
     */
    public SampleCommand() {
        super();
    }

    /**
     * The greetings command.
     * 
     * @param pName
     *            The name to greet
     * @return The greetings String
     */
    @Descriptor("Greet someone")
    public String greet(@Descriptor("the person to greet") final String pName) {
        return mTranslator.getLabel(GREET_TEXT, pName);
    }

    /**
     * Change the default locale.
     * 
     * @param session
     *            The command session
     * @param pLocaleCode
     *            The locale code
     */
    @Descriptor("Change locale")
    public void locale(final CommandSession session,
            @Descriptor("the code of the locale to set") final String pLocaleCode) {

        final Locale lLocale = getClosestCompatibleLocale(pLocaleCode);

        if (lLocale != null) {
            mResourcesManager.setUserLocale(lLocale);
            session.getConsole().println(
                    mTranslator.getLabel(LOCALE_CHANGED, lLocale.getDisplayName()));
        } else {
            session.getConsole().println(mTranslator.getLabel(LOCALE_ERROR, pLocaleCode));
        }
    }

    /**
     * Get the closest locale of the same language.
     * 
     * @param pLocaleCode
     *            The locale code
     * @return A matching locale, or <code>null</code> if language is not
     *         supported
     */
    private Locale getClosestCompatibleLocale(final String pLocaleCode) {
        final StringTokenizer lTokenikzer = new StringTokenizer(pLocaleCode, "_"); //$NON-NLS-1$
        final Map<String, Map<String, Locale>> lLocales = new HashMap<String, Map<String, Locale>>();

        // Filter on language
        final String lLang = lTokenikzer.nextToken();
        for (final Locale lLocale : Locale.getAvailableLocales()) {

            if (lLang.equalsIgnoreCase(lLocale.getLanguage())) {
                Map<String, Locale> lVariants = lLocales.get(lLocale.getCountry());
                if (lVariants == null) {
                    lVariants = new HashMap<String, Locale>();
                    lLocales.put(lLocale.getCountry().toLowerCase(), lVariants);
                }
                lVariants.put(lLocale.getVariant().toLowerCase(), lLocale);
            }
        }

        // Language may be unknown
        Locale lLocale = null;
        if (!lLocales.isEmpty()) {

            // Then, compare country
            String lCountry = ""; //$NON-NLS-1$
            if (lTokenikzer.hasMoreTokens()) {
                lCountry = lTokenikzer.nextToken().toLowerCase();
            }

            // Does country exist?
            Map<String, Locale> lVariants = lLocales.get(lCountry);
            if (lVariants == null) {
                // Use the raw language
                lVariants = lLocales.get(""); //$NON-NLS-1$
            }

            // Finally : variant
            String lVariant = ""; //$NON-NLS-1$
            if (lTokenikzer.hasMoreTokens()) {
                lVariant = lTokenikzer.nextToken().toLowerCase();
            }

            // Does variant exist?
            lLocale = lVariants.get(lVariant);
            if (lLocale == null) {
                lLocale = lVariants.get(""); //$NON-NLS-1$
            }
        }
        return lLocale;
    }

    /**
     * Display the current locale.
     * 
     * @param session
     *            The command session
     */
    @Descriptor("Display current locale")
    public void locale(final CommandSession session) {
        final Locale lLocale = mResourcesManager.getUserLocale();
        final String lText = mTranslator.getLabel(LOCALE_CURRENT, lLocale.getDisplayName());
        session.getConsole().println(lText);
    }

    /**
     * Bind the translator.
     * 
     * @param pTranslator
     *            The translator
     */
    protected void bindTranslator(final ITranslator pTranslator) {
        mTranslator = pTranslator;
    }

    /**
     * Unbind the translator.
     * 
     * @param pTranslator
     *            The translator
     */
    protected void unbindTranslator(@SuppressWarnings("unused") final ITranslator pTranslator) {
        mTranslator = null;
    }

    /**
     * Bind the resources manager.
     * 
     * @param pResourcesManager
     *            The resources manager
     */
    protected void bindResources(final IResourcesManager pResourcesManager) {
        mResourcesManager = pResourcesManager;
    }

    /**
     * Unbind the resources manager.
     * 
     * @param pResourcesManager
     *            The resources manager
     */
    protected void unbindResources(
            @SuppressWarnings("unused") final IResourcesManager pResourcesManager) {
        mResourcesManager = null;
    }
}
